#include "../include/mysqlquery.h"
#include "../../util/cpp/util/gwcommon.h"
#include <sstream>

GW_ImplementInterfaceSingleton(MysqlService);
GW_ImplementInterfaceSingleton(PreStmtInitializer);

/***************************************************************************************************************************/
MysqlService::MysqlConnection::MysqlConnection(const std::string& databaseIP, int databasePort, const std::string& schema, const std::string& usr, const std::string& psw):
m_bIsOpen(false),
m_dbConn(gwNULL),
m_schemaName(schema),
m_userName(usr),
m_passWord(psw)
{
	std::stringstream url;
        url<<"tcp://";
        url<<databaseIP;
        url<<":";
        url<<databasePort;

        m_strUrl = url.str();
}

/***************************************************************************************************************************/
MysqlService::MysqlConnection::~MysqlConnection()
{
	Close();
}

/***************************************************************************************************************************/
inline
bool
MysqlService::MysqlConnection::IsOpen() const
{
	return m_bIsOpen;
}

/***************************************************************************************************************************/
void
MysqlService::MysqlConnection::Open()
{
	sql::Driver* driver = get_driver_instance();
        if(driver != gwNULL)
        {
                m_dbConn = driver->connect(m_strUrl, m_userName, m_passWord);
                if(m_dbConn != gwNULL)
                {
                        m_dbConn->setSchema(m_schemaName);
						m_bIsOpen = true;
                }
        }
}

/***************************************************************************************************************************/
void
MysqlService::MysqlConnection::Close()
{
	gw_delete(m_dbConn);
	m_bIsOpen = false;
}

/***************************************************************************************************************************/
MysqlService::StmtPtr
MysqlService::MysqlConnection::CreateStatement()
{
	if(IsOpen() == true && m_dbConn != gwNULL)
	{
		return MysqlService::StmtPtr(m_dbConn->createStatement());
	}

	return MysqlService::StmtPtr();
}

/***************************************************************************************************************************/
MysqlService::PreStmtPtr
MysqlService::MysqlConnection::CreatePreparedStatement(const std::string &sql_sentence)
{
	if(IsOpen() == true && m_dbConn != gwNULL)
	{
		return MysqlService::PreStmtPtr(m_dbConn->prepareStatement(sql_sentence));
	}

	return MysqlService::PreStmtPtr();
}

/***************************************************************************************************************************/
MysqlService::stConnectionUnit::stConnectionUnit(MysqlConnPtr ptr):
ConnPtr(ptr)
{

}

/***************************************************************************************************************************/
MysqlService::MysqlService()
{
	GW_ConstructInterfaceSingleton;
}

/***************************************************************************************************************************/
MysqlService::~MysqlService()
{
	GW_DestructInterfaceSingleton;

	CloseAll();
}

/***************************************************************************************************************************/
void
MysqlService::OpenConnection(int id, const std::string &databaseIP, int databasePort, const std::string &schema, const std::string &usr, const std::string &psw)
{
	ConnectionMap::iterator itr = m_connMap.find(id);
	if(itr == m_connMap.end())
	{
		MysqlConnPtr new_connection(new MysqlConnection(databaseIP, databasePort, schema, usr, psw));
		new_connection->Open();
		if(new_connection->IsOpen() == true)
		{
		        stConnectionUnit* unit = new stConnectionUnit(new_connection);
		        m_connMap.insert(ConnectionMap::value_type(id, unit));
		        if(PreStmtInitializer::HasInstance() == true)
		        {
		                PreStmtInitializer::TargetMap& initmap = PreStmtInitializer::Instance()->m_mapInit[id];
		                for(PreStmtInitializer::TargetMap::iterator init_itr = initmap.begin();
		                    init_itr != initmap.end();
		                    ++init_itr)
		                {
		                        MysqlService::PreStmtPtr ptr = new_connection->CreatePreparedStatement(init_itr->first);
		                        unit->PreMap.insert(MysqlService::PreStmtPtrMap::value_type(init_itr->first, ptr));
		                        init_itr->second.operator =(ptr);
		                }
		        }
		}
	}
}

/***************************************************************************************************************************/
bool
MysqlService::IsConnectionOpen(int id) const
{
	ConnectionMap::const_iterator itr = m_connMap.find(id);
	if(itr != m_connMap.end())
	{
		return itr->second->ConnPtr->IsOpen();
	}

	return false;
}

/***************************************************************************************************************************/
void
MysqlService::CloseConnection(int id)
{
	ConnectionMap::const_iterator itr = m_connMap.find(id);
	if(itr != m_connMap.end())
	{
		delete itr->second;
		m_connMap.erase(id);
	}
}

/***************************************************************************************************************************/
void
MysqlService::CloseAll()
{
	for(ConnectionMap::iterator itr = m_connMap.begin(); itr != m_connMap.end(); ++itr)
	{
		delete itr->second;
	}

	m_connMap.clear();
}

/***************************************************************************************************************************/
MysqlService::StmtPtr
MysqlService::GetStatement(int connectionId)
{
	ConnectionMap::const_iterator itr = m_connMap.find(connectionId);
	if(itr != m_connMap.end())
	{
		return itr->second->ConnPtr->CreateStatement();
	}
	return MysqlService::StmtPtr();
}

/***************************************************************************************************************************/
MysqlService::PreStmtPtr
MysqlService::GetPreparedStatement(int connectionId, const std::string &sql_sentence)
{
	ConnectionMap::const_iterator itr = m_connMap.find(connectionId);
	if(itr != m_connMap.end())
	{
        PreStmtPtrMap::const_iterator preitr = itr->second->PreMap.find(sql_sentence);
        if(preitr != itr->second->PreMap.end())
            return preitr->second;
        else
        {
            MysqlService::PreStmtPtr new_pre = itr->second->ConnPtr->CreatePreparedStatement(sql_sentence);
            itr->second->PreMap.insert(PreStmtPtrMap::value_type(sql_sentence, new_pre));
            return new_pre;
        }
	}
	return MysqlService::PreStmtPtr();
}

/***************************************************************************************************************************/
PreStmtInitializer::PreStmtInitializer()
{
        GW_ConstructInterfaceSingleton;
}

/***************************************************************************************************************************/
PreStmtInitializer::~PreStmtInitializer()
{
        GW_DestructInterfaceSingleton;
        m_mapInit.clear();
}

/***************************************************************************************************************************/
void
PreStmtInitializer::Add(int conn_id, MysqlService::PreStmtPtr& p, const std::string& sql)
{
    m_mapInit[conn_id].insert(TargetMap::value_type(sql, gwBedrockUtil::gwReference<MysqlService::PreStmtPtr>(p)));
}
